﻿using System;

namespace LeRenard.Core.Time
{
    /// <summary>
    /// Specifies a range between a minimum and a maximum <see cref="DateTime"/> value.
    /// </summary>
    public sealed class DateTimeRange : IRange<DateTime>
    {
        #region Construction
        /// <summary>
        /// Creates a new <see cref="DateTimeRange"/> object.
        /// </summary>
        /// <param name="minimum">The minimum value of the range.</param>
        /// <param name="maximum">The maximum value of the range.</param>
        public DateTimeRange(DateTime minimum, DateTime maximum)
        {
            _minimum = minimum;
            _maximum = maximum;
        }

        /// <summary>
        /// Creates a new <see cref="DateTimeRange"/> object.
        /// </summary>
        /// <param name="minimum">The minimum value of the range.</param>
        public DateTimeRange(DateTime minimum)
            : this(minimum, DateTime.MaxValue)
        {
        }

        /// <summary>
        /// Creates a new <see cref="DateTimeRange"/> object.
        /// </summary>
        public DateTimeRange()
            : this(DateTime.MinValue)
        {
        }
        #endregion Construction

        #region IRange
        /// <summary>
        /// The minimum value of the range.
        /// </summary>
        public DateTime Minimum { get { return _minimum; } }
        private readonly DateTime _minimum;

        /// <summary>
        /// The maximum value of the range.
        /// </summary>
        public DateTime Maximum { get { return _maximum; } }
        private readonly DateTime _maximum;

        /// <summary>
        /// Determines whether the given value of <typeparamref name="T"/> is within range.
        /// </summary>
        /// <param name="value">The value to be checked.</param>
        /// <param name="isMinimumIncluded">Determines whether the <see cref="Minimum"/> range is included in the comparison.</param>
        /// <param name="isMaximumIncluded">Determines whether the <see cref="Maximum"/> range is included in the comparison. </param>
        /// <returns>True when in range, false when not in range.</returns>
        public bool IsInRange(DateTime value, bool isMinimumIncluded = true, bool isMaximumIncluded = true)
        {
            var isInRange =
                value >= (isMinimumIncluded ? _minimum : _minimum.AddMilliseconds(1)) &&
                value <= (isMaximumIncluded ? _maximum : _maximum.AddMilliseconds(-1));

            return isInRange;
        }
        #endregion IRange

        /// <summary>
        /// Returns a <see cref="String"/> that represents the object.
        /// </summary>
        /// <returns>A <see cref="String"/> that represents the object.</returns>
        public override string ToString()
        {
            return "[{0}: Minimum={1}, Maximum={2}]".FormatWith(
                    this.GetType().ToFriendlyName(),
                    ToStringHelper.For(this, x => x.Minimum.ToShortDateString()),
                    ToStringHelper.For(this, x => x.Maximum.ToShortDateString())
                );
        }
    }
}
